#!/usr/bin/env python3
# Created by han on 2016/7/4
import json
import os
import socket
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from module import public
from conf import db_conf


class myclient:
    def __init__(self, ip, port, name=None, size=None):
        self.ip = ip
        self.port = port
        self.name = name
        self.size = size        #每个用户注册后默认的一个大小为1024k

    def login(self, log_info):
        ip_port = (self.ip, self.port)
        sk = socket.socket()
        sk.connect(ip_port)
        sk.sendall(bytes(self.name,"utf8")) #发送用户名字
        sk.recv(1024)               #接受客户端发来的欢迎消息
        log_info_json = json.dumps(log_info)
        sk.sendall(bytes(log_info_json, "utf8"))
        ret = sk.recv(1024)
        return ret.decode()



    def cmd(self):
        """
        用户执行命令
        :return:
        """
        ip_port = (self.ip, self.port)
        sk = socket.socket()
        sk.connect(ip_port)
        home_name = self.name + "_" + self.size
        sk.sendall(bytes(home_name,"utf8")) #发送用户家目录名字
        recv_data = sk.recv(1024)
        print(recv_data.decode())
        while True:
            cmd_data = input("退出请输入【exit】>>: ").strip() #格式化命令数据
            if not cmd_data:      #如果输入命令为空，则重新输入
                continue
            if cmd_data == "exit":  #如果命令为exit 为退出，终端本次通话
                break
            if not cmd_data.startswith("put") and not cmd_data.startswith("get"):       #如果用户输入的字段不是put或者get开头
                cmd_ret = self.client_shell(sk, cmd_data)     #那就执行ssh的模块
                if cmd_ret:                #如果有返回值，证明命令执行成功
                    print(cmd_ret.decode())   #打印命令的返回值
                else:
                    break           #否则表示有异常，退出本次循环，这个应该不会出现，因为服务器端不会有异常发生，因为客户端这里都做了判断了
            else:                              #用户输入的字段是put或者get开头
                cmd_list = cmd_data.split()     #拆分命令
                if len(cmd_list) < 2:           #如果长度小于2 这名上传或下载的格式不对
                    msg = "type error"
                    public.color_msg(31, msg, 40)
                    continue                    #让用户重新输入
                task_type = cmd_list[0]         #如果不小于2，获取用户的操作类型
                if hasattr(self, "client_%s" % task_type):      #检验这个功能是否存在
                    func = getattr(self, "client_%s" % task_type)   #如果存在则获取这个方法
                    abs_filepath = cmd_list[1]                      #用户要上传的文件
                    if task_type == "put":
                        if os.path.isfile(abs_filepath):                #判断文件如果存在
                            func(sk, abs_filepath)                      #执行
                        else:
                            msg = "file not exist"
                            public.color_msg(31, msg, 40)
                    else:
                        func(sk, abs_filepath)
                else:
                    msg = "unsupported : %s" % task_type
                    public.color_msg(31, msg, 40)
        sk.close()

    def client_register(self, user_info):
        """
        注册用户
        :param user_info:
        :return:
        """
        ip_port = (self.ip, self.port)
        sk = socket.socket()
        a = sk.connect(ip_port)                 #接通电话
        user_dict = json.loads(user_info)
        sk.sendall(bytes(user_dict["name"], "utf8"))
        welcome_msg = sk.recv(1024)
        print(welcome_msg.decode())
        sk.sendall(bytes(user_info, "utf8"))               #传送要干的事情
        register_ret = sk.recv(4096)             #接收确认信息
        if register_ret.decode() == "success":
            msg = "register success"
            public.color_msg(32, msg, 40)
            sk.close()
            return True             #如果服务器顶端返回成功，则返回真

    def client_shell(self, socket_obj, cmd_data):
        """
        执行shell命令的功能
        :param socket_obj:socket对象
        :param cmd_data: 用户输入的命令
        :return: socket server端返回的结果bytes类型
        """
        socket_obj.sendall(bytes(cmd_data, "utf8"))  # 正常就传送命令数据
        ready_data = socket_obj.recv(1024)  # 然后接受准备数据
        raw_data = ready_data.decode()  # 解码准备接收数据
        # print(raw_data)
        if raw_data.startswith("ready"):  # 如果这个数据开头是ready开始的
            raw_len = int(raw_data.split("|")[-1])  # 则获取长度
            client_ready = bytes("start", "utf8")  # 准备开始数据
            socket_obj.sendall(client_ready)  # 发送可以接受数据的信号
            data_len = 0  # 初始化长度
            data = b""  # 初始化数据
            while data_len < raw_len:
                recv_data = socket_obj.recv(1024)  # 循环接受数据
                data_len += len(recv_data)  # 循环增加数据长度
                data += recv_data  # 循环将数据相加
                print("all : %d (byte) , local : %d (byte)" % (raw_len, data_len))      #显示总大小和单次的大小
            return data
        else:
            error_data = socket_obj.recv(1024)
            print(error_data.decode())
            return False

#D:\mydrivers\update\DirectX_Jun2010_redist.exe
#D:\影音\python视频\day9\oldboy-1.avi

    def client_put(self, socket_obj, file):
        """
        上传文件
        :param socket_obj: socket对象
        :param file: 上传的文件
        :return:
        """
        file_size = os.stat(file).st_size           #查看文件的大小
        msg = "Upload File: %s , size: %s\n" % (file, file_size)        #打印文件名称和文件大小
        public.color_msg(36, msg, 50)
        md5 = public.get_file_md5(file)             #计算md5
        info = {"action": "put", "name": file, "size": file_size, "md5": md5}   #准备以json格式传输
        file_info = json.dumps(info)
        socket_obj.sendall(bytes(file_info, "utf8"))                            #传输文件信息
        ready_flag = socket_obj.recv(1024)                                      #接收服务器端发来的确认信息，要上传的文件状态
        confirm_data = json.loads(ready_flag.decode())                                   #服务器传来的json
        if confirm_data["status"] == "0":                                       #如果status为0代表文件已经存在且完整
            msg = "file is exist"
            public.color_msg(31, msg, 40)
            return False
        elif confirm_data["status"] == "1":                                     #如果status为1 代表文件不存在
            send_size = public.send_file(socket_obj, confirm_data, file, file_size)         #开始上传
            socket_obj.recv(4096)
            if send_size == file_size:                                      #如果传输的大小，等于文件大小
                msg = "send success"                                        #成功
                public.color_msg(32, msg, 40)
                return True
            else:
                msg = " transmission interruption "
                public.color_msg(31, msg, 40)
        elif confirm_data["status"] == "2":                     #如果是2，有文件，但是文件不全
            print("续传")
            send_size = public.send_file(socket_obj, confirm_data, file, file_size)
            socket_obj.recv(4096)               #接受服务器传来的确认信息
            if send_size == file_size:  # 如果传输的大小，等于文件大小
                msg = "send success"  # 传输成功
                public.color_msg(32, msg, 40)
                return True
            else:
                msg = " transmission interruption "         #传输不完整
                public.color_msg(31, msg, 40)
        elif confirm_data["status"] == "3":                 #如果是3则是文件超出了可以上传的界限
            may_size = confirm_data["max"]                  #获取可上传大小
            file_size = confirm_data["file"]                #获取文件大小
            msg = "Upload Fail --- You May Update [ %s ] b ,But The File Is [ %s ] b " % (may_size, file_size)
            public.color_msg(31, msg, 40)
            return False

    def client_get(self, socket_obj, file):
        """
        下载文件
        :param socket_obj: socket 对象
        :param file: 要下载的文件名
        :return:
        """
        # print(file)
        client_space = db_conf.user_space(self.name, self.size)     #获取用户客户端加目录
        user_size = int(self.size)*1024*1024                #用户的可用空间        #获取可用空间
        # print(user_size, type(user_size))
        use_size = public.dir_size(client_space)           #获取已经使用的空间
        may_size = user_size - use_size                    #计算当前可用的空间
        abs_file_name = client_space + "/" + file          #绝对路径的文件名
        msg = {"action": "get", "file_name": file, "client_md5": 0, "client_size":0, "client_seek": 0}#定义好传送的初始化格式
        if not os.path.exists(abs_file_name):                                  #如果本地文件不存在
            msg_json = json.dumps(msg)
            socket_obj.sendall(bytes(msg_json, "utf8"))                                  #第一次发送
            recv_json = socket_obj.recv(4096)                                        #第一次接收
            recv_msg = json.loads(recv_json.decode())
            if recv_msg["status"] == "1":                  #如果status 显示为1 代表服务器没有这个文件
                msg = "no this file"
                public.color_msg(31, msg, 40)                       #显示报错退出
            elif recv_msg["status"] == "4":                      #如果status 显示4  表示要接受一个完整的文件
                if recv_msg["server_size"] < may_size:
                    socket_obj.sendall(bytes("start", "utf8"))              #第二次发送 确认发送消息
                    public.save_file(socket_obj, recv_msg["server_size"], 0, abs_file_name, "wb", recv_msg["server_md5"]) #第二次接收完整信息
                else:
                    socket_obj.sendall(bytes("stop", "utf8"))
                    error_msg = "space not enough"
                    public.color_msg(31, error_msg, 40)
        else:                       #如果文件存在
            client_md5 = public.get_file_md5(abs_file_name)     #查看文件的md5值
            # print("-----------",client_md5,type(client_md5))
            msg["client_md5"] = client_md5                      #把本地md5值发送给server，好让server判断文件是否完整
            msg["client_size"] = os.stat(abs_file_name).st_size
            with open(abs_file_name, "ab") as local_file:
                local_seek = local_file.tell()
            msg["client_seek"] = local_seek
            # print(msg)
            msg_json = json.dumps(msg)
            socket_obj.sendall(bytes(msg_json, "utf8"))              #第一次发送
            recv_json = socket_obj.recv(2048)                   #第一次接收
            recv_msg = json.loads(recv_json.decode())
            if recv_msg["status"] == "0":                         #如果server发来的是0 就代表完整
                error_msg = "file exist"
                public.color_msg(32, error_msg, 40)
            elif recv_msg["status"] == "2":                          #如果服务器发来的是2 就代表需要继续接收
                print("续传")
                if recv_msg["server_size"] > may_size:
                    socket_obj.sendall(bytes("stop","utf8"))
                    msg = "space not enough"
                    public.color_msg(31, msg, 40)
                else:
                    socket_obj.sendall(bytes("start","utf8"))
                    public.save_file(socket_obj, recv_msg["server_size"], msg["client_size"], abs_file_name, "ab", recv_msg["server_md5"])                                                   #继续接收需要获得发送给服务器seek值






#   D:\资料\python\oldboy13\jobs\day9\myftp\ftp_client\space\wan_10\tkxy1.mp3